#include <iostream>
#include <fstream>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
//#include <windows.h> // 图像读取的头文件
#include<vector>
#include<string>
#pragma pack(1)					//设定变量以n字节对齐方式
#define _CRT_SECURE_NO_WARNINGS
using namespace std;

typedef unsigned char BYTE;		//1个字节
typedef unsigned short WORD;	//2个字节
typedef unsigned int DWORD;		//4个字节
typedef unsigned long LONG;		//4个字节

/*位图文件头定义 14个字节*/
typedef struct tagBITMAPFILEHEADER {
	WORD bfType;				//位图文件的类型，必须为BM(0-1字节)
	DWORD bfSize;				//位图文件的大小，以字节为单位(2-5字节) 
	WORD bfReserved1;			//位图文件保留字，必须为0(6-7字节)
	WORD bfReserved2;			//位图文件保留字，必须为0(8-9字节)
	DWORD bfOffBits;			//位图数据的起始位置，以相对于位图(10-13字节)
} BITMAPFILEHEADER;

/*位图信息头定义 40字节*/
typedef struct tagBITMAPINFOHEADER {
	DWORD biSize;				//本结构所占用字节数(14-17字节) 
	LONG biWidth;				//位图的宽度，以像素为单位(18-21字节) 
	LONG biHeight;				//位图的高度，以像素为单位(22-25字节) 
	WORD biPlanes;				//目标设备的级别，必须为1(26-27字节) 
	WORD biBitCount;			//每个像素所需的位数，必须是1(双色)、4(16色)、8(256色)、24(真彩色)，(28-29字节)
	DWORD biCompression;		//位图压缩类型，必须是0(不压缩)、1(BI_RLE8压缩类型)、2(BI_RLE4压缩类型)，(30-33字节)
	DWORD biSizeImage;			//位图的大小，以字节为单位(34-37字节)
	LONG biXPelsPerMeter;		//位图水平分辨率，每米像素数(38-41字节)
	LONG biYPelsPerMeter;		//位图垂直分辨率，每米像素数(42-45字节)
	DWORD biClrUsed;			//位图实际使用的颜色表中的颜色数(46-49字节)
	DWORD biClrImportant;		//位图中重要的色彩数,如果该值为零，则认为所有的颜色都是重要的(50-53字节)
} BITMAPINFOHEADER;

/*调色板定义 4字节*/
typedef struct tagRGBQUAD {
	BYTE rgbBlue;				//蓝色的亮度(值范围为0-255)
	BYTE rgbGreen;				//绿色的亮度(值范围为0-255)
	BYTE rgbRed;				//红色的亮度(值范围为0-255)
	BYTE rgbReserved;			//保留，必须为0 
} RGBQUAD;
// 定义一个结构体来读取BMP的信息

typedef struct {
	// 定义文件头信息
	BITMAPFILEHEADER header;
	// 定义位图信息头
	BITMAPINFOHEADER info;
	// 定义彩色表
	RGBQUAD rgb[256];
	// 定义位图数据指针
	unsigned char* data;
} BMP;

typedef vector<vector<int> > table_i;
typedef vector<vector<string> > table_s;
double transform(char*str){
	int a=0,b=0;int pd=1;
	double re;
	for(int i=0;i<strlen(str);i++){
		if(str[i]=='.'){
		pd=0;	
		break;
		}
	}
	if(pd==0){
	sscanf(str,"%d.%d",&a,&b);
	int weishu=0;
	while(b>0){
		b/=10;
		weishu++;
	}
	re=a*1.0+b*1.0/weishu;
	return re;}
	else{
		sscanf(str,"%d",&a);
		re=a*1.0;
		return re;
	}
}
//算法处理部分 
void Gl(int height,int width,int min,int max,unsigned char *p[],unsigned char *np[],char *argv[]){
	int i=0,j=0;
	if( strcmp(argv[2],"-l")==0 ){
			printf("非法操作！\n");
			exit(1);
			}	
	printf("您选择了灰度变换！\n"); 
			double a,b,c,d,min1,max1;
		printf("您选择了线性变换！\n");
		a=transform(argv[3]);
		printf("变换前的突出区间：\na=%lf",a);
		b=transform(argv[4]);
		printf("b=%lf",b);
		c=transform(argv[5]);
		printf("变换后的突出区间：\nc=%lf",c);
		d=transform(argv[6]);
		printf("d=%lf",d); 
		min1=transform(argv[7]);
		max1=transform(argv[8]);
		printf("变换后的区间的下界为:%lf,上界为%lf",min1,max1); 
		if(a>b||c>b||min1>max1||a<0||b>255||c<0||d>255||min1<0||max1>255){
			printf("参数异常！\n");
			exit(1);
		}
		else{
			for(j=0;j<width;j++){
				for(i=0;i<height;i++){
					if(p[i][j]<a) np[i][j]= round((c-min1)/(a-min)*p[i][j]);
					else if(p[i][j]>b) np[i][j]=round(( (max1-d) / (max-b) )*(p[i][j]-b)+d);
					else np[i][j]=round(( (d-c)/(b-a) )*(p[i][j]-a)+c);
				}
			}
		}
	
}

void Ge(int height,int width,int min,int max,unsigned char *p[],unsigned char *np[],char*argv[]){
	int i=0,j=0;
	if( strcmp(argv[2],"-e")==0 ){
			printf("非法操作！\n");
			exit(1);
		}
	printf("您选择了灰度变换！\n");
	printf("您选择了对数变换！\n");
			double a,b,c;
			a=transform(argv[3]),b=transform(argv[4]),c=transform(argv[5]);
			printf("参数a=%lf,b=%lf,c=%lf",a,b,c);
			if(c<0||c==1||b<=0) {
				printf("参数异常！\n");
				exit(1);
			}
			else{
				for(j=0;j<width;j++){
					for(i=0;i<height;i++){
						np[i][j]=round( a+log(p[i][j]+1.0)/(b*log(c)) );
						if(np[i][j]>255) np[i][j]=255;
						else if(np[i][j]<0) np[i][j]=0;
					}
				}
			}
		
	
}
// 计算灰度值的概率分布
void CP(int height,int width,unsigned char *p[], double probability[]) { 
    int total = height * width;
    // 初始化概率分布数组为0
    for (int i = 0; i < 256; i++) {
        probability[i] = 0;
    }
    // 统计每个灰度值出现的次数
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            int pr = p[i][j];
            probability[pr]++;
        }
    }
    // 计算概率分布
    for (int i = 0; i < 256; i++) {
        probability[i] /= total;
    }
}

// 计算累计分布函数
void CDF(double probability[], double cdf[]) {
    cdf[0] = probability[0];
    for (int i = 1; i < 256; i++) {
        cdf[i] = cdf[i - 1] + probability[i];
    }
}

// 对单个像素进行直方图均衡化变换
int Single(int q, double probability[], double cdf[]) {
    int l = 256;
    // 根据公式计算均衡化后的灰度值
    int q1 = round((l - 1) * cdf[q]);

    return q1;
}

void H(int height,int width,unsigned char *p[],unsigned char *np[]) {
    printf("您选择了图像均衡化！\n");

    double probability[256]={0};
    double cdf[256]={0};
    
    CP(height,width,p,probability);
    CDF(probability, cdf);

    // 对每个像素进行直方图均衡化变换
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            np[i][j] = Single(p[i][j], probability, cdf);
        }
    }
}


void L(unsigned char *p[], int height, int width, int s, unsigned char *np[]) {//此处宏定义为测试，实际需输入
    int h = s / 2;
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            int sum = 0,count = 0;
            
            // 遍历当前像素的邻域窗口
            for (int m = i - h; m <= i + h; m++) {
                for (int n = j - h; n <= j + h; n++) {
                    // 确保邻域像素索引在合法范围内
                    if (m >= 0 && m < height && n >= 0 && n < width) {
                        sum += p[m][n];
                        count++;
                    }
                }
            }

            // 计算平均值并更新输出图像的像素值
            np[i][j] = sum / count;
        }
    }
}

void S(int height,int width,unsigned char *p[],unsigned char *np[],char *argv[]){
	printf("您选择了局部图像平滑！");
	if( strcmp(argv[3],"-3")==0 ){
		printf("您选择了3*3窗口进行平滑！\n");
		L(p, height, width, 3, np);
	}
	else if(  strcmp(argv[3],"-5")==0 ){
		printf("您选择了5*5窗口进行平滑！\n");
		L(p, height, width, 5, np);
	}
	else{
		printf("非法操作！\n"); 
		exit(1);
	}
}
int main(int argc,char*argv[])
{
		const char *path;
		const char *graytrans;
	if(argc==8){
	path=argv[6];
	graytrans=argv[7];
	}
	else if(argc==11){
	path=argv[9];
	graytrans=argv[10];
	}
	else if(argc==4){
		path=argv[2];
		graytrans=argv[3];
	}
	else{
		path=argv[2];
		graytrans=argv[4];
	}
	FILE* fo;			//打开bmp文件
	FILE* fs;			//存储bmp文件 

	BITMAPFILEHEADER FileHeader;		//bmp的文件头 
	BITMAPINFOHEADER InfoHeader;		//bmp的信息头 
	
	fo = fopen(path, "rb+");			//读bmp文件 
	fs = fopen(graytrans, "wb+");				//写bmp文件 

	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fo);		//把bmp文件头读出 
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fo);		//把bmp信息头读出 

	if (InfoHeader.biBitCount == 8) {					//当为灰度图像时 

		int width = InfoHeader.biWidth;			//位图的宽度 
		int height = InfoHeader.biHeight;		//位图的高度 
		int graywidth = InfoHeader.biWidth;			//灰度变换后的宽度 
		int grayheight = InfoHeader.biHeight ;		//灰度变换后的高度 

		InfoHeader.biWidth = graywidth;
		InfoHeader.biHeight = grayheight;
		InfoHeader.biSizeImage = graywidth * grayheight;
		FileHeader.bfSize = 54 + InfoHeader.biSizeImage;  //编辑文件头和信息头

		RGBQUAD color[256];		//8bit的有调色板 
		fread(&color[0], sizeof(RGBQUAD), 256, fo);//读取调色盘数据

		unsigned char* pColorData = (unsigned char*)malloc(height * width);		//原始图像数据的空间 
		unsigned char* pColorDataWrite = (unsigned char*)malloc(grayheight * graywidth);		//转换后图像数据的空间 

		memset(pColorData, 0, height * width);					//将数据空间全部置空
		memset(pColorDataWrite, 0, grayheight * graywidth);	

		fread(pColorData, 1, height * width, fo);		//从bmp文件中读取数据 
		//创建二维数组，便于后续处理 
		unsigned char** arr = (unsigned char**)malloc(height * sizeof(unsigned char*));
		for (int i = 0; i < height; i++) {
		    arr[i] = (unsigned char*)malloc(width * sizeof(unsigned char));
	    }
		unsigned char** newarr = (unsigned char**)malloc(height * sizeof(unsigned char*));
		for (int i = 0; i < height; i++) {
		    newarr[i] = (unsigned char*)malloc(width * sizeof(unsigned char));
	    }
		// 循环读取像素值
		int min=256,max=-1;//找到灰度值的最大值，最小值 
		for (int i = 0; i < height; i++){
			for (int j = 0; j < width; j++){
				arr[i][j]=pColorData[i*width+j];
				if(pColorData[i*width+j]>max) max=pColorData[i*width+j];
				if(pColorData[i*width+j]<min) min=pColorData[i*width+j];
				newarr[i][j]=arr[i][j];
			}
		}
		//二维数组传入函数部分 
		unsigned char *p[height];//定义一个指针数组，其含有3个指针指向int类型
	    for(int i=0;i<height;i++) p[i]=arr[i];//让3个指针分别指向ab的3行
	    unsigned char *np[height];//定义一个指针数组，其含有3个指针指向int类型
	    for(int i=0;i<height;i++) np[i]=newarr[i];//让3个指针分别指向ab的3行
		
		//printf("min=%d max=%d\n",min,max); 
		//开始进行转化 
		if(argc==11){
			Gl(height,width,min,max,p,np,argv);//灰度变化 线性
		}
	    if( argc==8 ){//采用灰度变换 非线性
		   Ge(height,width,min,max,p,np,argv);
	    }
	    else if(  argc==4){//图像均衡化 
		    H(height,width,p,np);
	    }
	    else if( argc==5 ){//图像平滑 
		    S(height,width,p,np,argv);
	    }
	    else{
		    printf("非法操作！\n");
		    exit(1);
	    }
        // 循环读写像素值
		for (int i = 0; i < height; i++){
			for (int j = 0; j < width; j++){
				pColorDataWrite[i*width+j]=newarr[i][j];
			}
		}

		fwrite(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fs);		//写文件头 
		fwrite(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fs);		//写信息头 
		fwrite(&color[0], sizeof(RGBQUAD), 256, fs);				//写调色板 
		fwrite(pColorDataWrite, 1, grayheight * graywidth, fs);		//写操作后的数据 

		free(pColorData);			//释放空间
		free(pColorDataWrite);
		free(arr);
		free(newarr);

		cout << "bmp图像灰度图像变换成功！" << endl << endl;

	}
	else {
		cout << "这不是bmp格式的灰度图像。" << endl << endl;
	}

	fclose(fo);			//关闭文件 
	fclose(fs);

	return 0;
}